//
//  LBLimitedTextField.m
//  LBLimitedTextField
//
//  Created by 刘文扬 on 17/6/28.
//  Copyright © 2017年 lianj. All rights reserved.
//

#import "LBLimitedTextField.h"
#import <objc/message.h>

@interface LBLimitedTextField ()<UITextFieldDelegate>

/** originalDelegate*/
@property(nonatomic, weak) id<UITextFieldDelegate> originalDelegate;

@property(nonatomic, weak)id originalTarget;

/** originSel*/
@property(nonatomic,assign)SEL originalSel;


@end

@implementation LBLimitedTextField

-(void)awakeFromNib
{
    [super awakeFromNib];
    [self configue];
}

-(instancetype)init
{
    self = [super init];
    if (self) {
        
        [self configue];
    }
    return self;
}

-(void)configue
{
    
    self.maxTextLength = NSIntegerMax;
    
    self.limitedIntNumber = NSIntegerMax;
    self.limitedFloatNumber = 2;
    
    self.delegate = self;
    
    [self addTarget:self action:@selector(textChange:) forControlEvents:UIControlEventEditingChanged];
}

-(void)setInputType:(LBLimitedTextFieldType)inputType
{
    _inputType = inputType;
    switch (inputType) {
        case LBLimitedTextFieldDefault:
        {
            self.keyboardType = UIKeyboardTypeDefault;
        }
            break;
        case LBLimitedTextFieldInt:
        {
            self.keyboardType = UIKeyboardTypeNumberPad;
        }
            break;
        case LBLimitedTextFieldFloat:
        {
            self.keyboardType = UIKeyboardTypeDecimalPad;
        }
            break;
            
        default:
            break;
    }
}


-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
    
    BOOL isDeleting = range.length > 0 && string.length <= 0;
    
    
    
    if (!self.canEmoji) {
        
        /// 过滤emoji
        // 忽略系统默认的emoji键盘
        if ([NSString nowIsEmojiKeyBorad]) {
            return NO;
        }
        
        // 验证string的emoji字符
        if ([string containEmoji]) {
            return NO;
        }
    }

    
    
    if (isDeleting || textField.markedTextRange) {
        
        if (self.originalDelegate && [self.originalDelegate respondsToSelector:_cmd]) {
            
            return [self.originalDelegate textField:textField shouldChangeCharactersInRange:range replacementString:string];
            
        }
        
        return YES;
    }else{
        NSMutableString *matchStr = [NSMutableString stringWithString:textField.text];
        [matchStr insertString:string atIndex:range.location];
        BOOL isMatch = NO;
        switch (self.inputType) {
            case LBLimitedTextFieldInt:
            {
                
                isMatch = [LBLimitedTextFieldMatcher matchIntWithLimitedIntNumber:self.limitedIntNumber text:matchStr];
                return isMatch;
            }
                break;
            case LBLimitedTextFieldFloat:
            {
                isMatch = [LBLimitedTextFieldMatcher matchFloatWithLimitedIntNumber:self.limitedIntNumber floatNumber:self.limitedFloatNumber text:matchStr];
                return isMatch;
            }
                break;
                
                
            default:
                isMatch = YES;
                break;
        }
        
        if (!isMatch) {
            if (self.lb_degegate && [self.lb_degegate respondsToSelector:@selector(limitedTextFieldOverLimitedWithTextField:textFieldType:maxTextLength:)]) {
                [self.lb_degegate limitedTextFieldOverLimitedWithTextField:self textFieldType:self.inputType maxTextLength:self.maxTextLength];
            }
        }
//        return isMatch;
    }
    
    
    if (range.location >= self.maxTextLength) {
        //防御措施,不知为何,输入英文的时候,点击联想的时候,即使return no也会显示
        textField.text = [textField.text substringToIndex:self.maxTextLength];
        if (self.lb_degegate && [self.lb_degegate respondsToSelector:@selector(limitedTextFieldOverLimitedWithTextField:textFieldType:maxTextLength:)]) {
            [self.lb_degegate limitedTextFieldOverLimitedWithTextField:self textFieldType:self.inputType maxTextLength:self.maxTextLength];
        }
        return NO;
    }
    
    if (self.originalDelegate && [self.originalDelegate respondsToSelector:_cmd]) {

        return [self.originalDelegate textField:textField shouldChangeCharactersInRange:range replacementString:string];
        
    }


    return YES;
}


-(void)textChange:(UITextField *)textField
{
    if (self.originalTarget) {
        if ([self.originalTarget respondsToSelector:self.originalSel]) {
            
            IMP imp = [self.originalTarget methodForSelector:self.originalSel];
            
            void (*func)(id, SEL, id) = (void *)imp;
            
            func(self.originalTarget, self.originalSel, self);
        }
        
    }
    
    if (!textField.markedTextRange) {
        //输入中文拼音的时候代理无法监听
        if (textField.text.length > self.maxTextLength) {
            textField.text = [textField.text substringToIndex:self.maxTextLength];
            if (self.lb_degegate && [self.lb_degegate respondsToSelector:@selector(limitedTextFieldOverLimitedWithTextField:textFieldType:maxTextLength:)]) {
                [self.lb_degegate limitedTextFieldOverLimitedWithTextField:self textFieldType:self.inputType maxTextLength:self.maxTextLength];
            }
        }
        
        if (self.lb_degegate && [self.lb_degegate respondsToSelector:@selector(limitedTextField:textCount:)]) {
            [self.lb_degegate limitedTextField:self textCount:textField.text.length];
        }
    }

}


-(void)setMarkedText:(NSString *)markedText selectedRange:(NSRange)selectedRange
{
    
    //        //防止在输入法中打出"哈哈"显示出来的"😝",代理
    //        //-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
    //        //无法监听
    
    //将含有emoji表情的文字替换为@""
    
    
    
    NSString *noEmojiMarkedText = markedText;
    if (!self.canEmoji) {
        noEmojiMarkedText = [markedText converStrEmoji:markedText];
    }
    [super setMarkedText:noEmojiMarkedText selectedRange:selectedRange];
}

#pragma mark - 重写

-(void)setDelegate:(id<UITextFieldDelegate>)delegate
{
    self.originalDelegate = delegate != self ? delegate : nil;
    [super setDelegate:delegate ? self : nil];
}

-(void)addTarget:(id)target action:(SEL)action forControlEvents:(UIControlEvents)controlEvents
{
    if (controlEvents == UIControlEventEditingChanged) {
        if (target != self) {
            self.originalTarget = target;
            self.originalSel = action;
        }else{
            [super addTarget:target action:action forControlEvents:controlEvents];
        }
    }else{
        [super addTarget:target action:action forControlEvents:controlEvents];
    }
    
}



- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    NSMethodSignature *result = [(id)self.originalDelegate methodSignatureForSelector:aSelector];
    return result;
}


- (void)forwardInvocation:(NSInvocation *)anInvocation {
    if ([(id)self.originalDelegate respondsToSelector:anInvocation.selector]) {
        [anInvocation invokeWithTarget:(id)self.originalDelegate];
    }
}

- (BOOL)respondsToSelector:(SEL)aSelector {
    BOOL a = [super respondsToSelector:aSelector];
    BOOL c = [self.originalDelegate respondsToSelector:aSelector];
    BOOL result = a || c;
    return result;
}




@end

@implementation LBLimitedTextFieldMatcher


+(BOOL)matchIntWithLimitedIntNumber:(NSInteger)limitedIntNumber text:(NSString *)text
{
    NSString * regex = [NSString stringWithFormat:@"[0-9]{0,%ld}",(long)limitedIntNumber];
    NSPredicate *pred = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", regex];
    BOOL result = [pred evaluateWithObject:text];
    return result;
}

+(BOOL)matchFloatWithLimitedIntNumber:(NSInteger)limitedIntNumber floatNumber:(NSInteger)floatNumber text:(NSString *)text
{
    NSInteger limitedPrefix = limitedIntNumber;
    NSInteger limitedSuffix = floatNumber;
    
    
    
    // 1.匹配以0开头的数字
    NSPredicate *matchZero = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", @"^[0][0-9]+$"];
    // 2.匹配两位小数、整数
    
    NSString *regex = [NSString stringWithFormat:@"^\\d{0,%ld}$|^(\\d{0,%ld}[.][0-9]{0,%ld})$", (long)limitedPrefix, (long)limitedPrefix, (long)limitedSuffix];
    
    NSPredicate *matchValue = [NSPredicate predicateWithFormat:@"SELF MATCHES %@",regex];
    BOOL isZero = ![matchZero evaluateWithObject:text];
    BOOL isCorrectValue = [matchValue evaluateWithObject:text];
    return isZero && isCorrectValue ? YES : NO;
}


@end



@implementation NSString (LBEmoji)


- (BOOL) emojiInUnicode:(short)code
{
    if (code == 0x0023
        || code == 0x002A
        || (code >= 0x0030 && code <= 0x0039)
        || code == 0x00A9
        || code == 0x00AE
        || code == 0x203C
        || code == 0x2049
        || code == 0x2122
        || code == 0x2139
        || (code >= 0x2194 && code <= 0x2199)
        || code == 0x21A9 || code == 0x21AA
        || code == 0x231A || code == 0x231B
        || code == 0x2328
        || code == 0x23CF
        || (code >= 0x23E9 && code <= 0x23F3)
        || (code >= 0x23F8 && code <= 0x23FA)
        || code == 0x24C2
        || code == 0x25AA || code == 0x25AB
        || code == 0x25B6
        || code == 0x25C0
        || (code >= 0x25FB && code <= 0x25FE)
        || (code >= 0x2600 && code <= 0x2604)
        || code == 0x260E
        || code == 0x2611
        || code == 0x2614 || code == 0x2615
        || code == 0x2618
        || code == 0x261D
        || code == 0x2620
        || code == 0x2622 || code == 0x2623
        || code == 0x2626
        || code == 0x262A
        || code == 0x262E || code == 0x262F
        || (code >= 0x2638 && code <= 0x263A)
        || (code >= 0x2648 && code <= 0x2653)
        || code == 0x2660
        || code == 0x2663
        || code == 0x2665 || code == 0x2666
        || code == 0x2668
        || code == 0x267B
        || code == 0x267F
        || (code >= 0x2692 && code <= 0x2694)
        || code == 0x2696 || code == 0x2697
        || code == 0x2699
        || code == 0x269B || code == 0x269C
        || code == 0x26A0 || code == 0x26A1
        || code == 0x26AA || code == 0x26AB
        || code == 0x26B0 || code == 0x26B1
        || code == 0x26BD || code == 0x26BE
        || code == 0x26C4 || code == 0x26C5
        || code == 0x26C8
        || code == 0x26CE
        || code == 0x26CF
        || code == 0x26D1
        || code == 0x26D3 || code == 0x26D4
        || code == 0x26E9 || code == 0x26EA
        || (code >= 0x26F0 && code <= 0x26F5)
        || (code >= 0x26F7 && code <= 0x26FA)
        || code == 0x26FD
        || code == 0x2702
        || code == 0x2705
        || (code >= 0x2708 && code <= 0x270D)
        || code == 0x270F
        || code == 0x2712
        || code == 0x2714
        || code == 0x2716
        || code == 0x271D
        || code == 0x2721
        || code == 0x2728
        || code == 0x2733 || code == 0x2734
        || code == 0x2744
        || code == 0x2747
        || code == 0x274C
        || code == 0x274E
        || (code >= 0x2753 && code <= 0x2755)
        || code == 0x2757
        || code == 0x2763 || code == 0x2764
        || (code >= 0x2795 && code <= 0x2797)
        || code == 0x27A1
        || code == 0x27B0
        || code == 0x27BF
        || code == 0x2934 || code == 0x2935
        || (code >= 0x2B05 && code <= 0x2B07)
        || code == 0x2B1B || code == 0x2B1C
        || code == 0x2B50
        || code == 0x2B55
        || code == 0x3030
        || code == 0x303D
        || code == 0x3297
        || code == 0x3299
        // 第二段
        || code == 0x23F0) {
        return YES;
    }
    return NO;
}

/**
 * 一种非官方的, 采用私有Unicode 区域
 * e0 - e5  01 - 59
 */
- (BOOL) emojiInSoftBankUnicode:(short)code
{
    return ((code >> 8) >= 0xE0 && (code >> 8) <= 0xE5 && (Byte)(code & 0xFF) < 0x60);
}




- (BOOL)containEmoji
{
    NSUInteger len = [self lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
    if (len < 3) {  // 大于2个字符需要验证Emoji(有些Emoji仅三个字符)
        return NO;
    }
    
    // 仅考虑字节长度为3的字符,大于此范围的全部做Emoji处理
    NSData *data = [self dataUsingEncoding:NSUTF8StringEncoding];
    
    Byte *bts = (Byte *)[data bytes];
    Byte bt;
    short v;
    for (NSUInteger i = 0; i < len; i++) {
        bt = bts[i];
        
        if ((bt | 0x7F) == 0x7F) {  // 0xxxxxxx  ASIIC编码
            continue;
        }
        if ((bt | 0x1F) == 0xDF) {  // 110xxxxx  两个字节的字符
            i += 1;
            continue;
        }
        if ((bt | 0x0F) == 0xEF) {  // 1110xxxx  三个字节的字符(重点过滤项目)
            // 计算Unicode下标
            v = bt & 0x0F;
            v = v << 6;
            v |= bts[i + 1] & 0x3F;
            v = v << 6;
            v |= bts[i + 2] & 0x3F;
            
            //            NSLog(@"%02X%02X", (Byte)(v >> 8), (Byte)(v & 0xFF));
            
            if ([self emojiInSoftBankUnicode:v] || [self emojiInUnicode:v]) {
                return YES;
            }
            
            i += 2;
            continue;
        }
        if ((bt | 0x3F) == 0xBF) {  // 10xxxxxx  10开头,为数据字节,直接过滤
            continue;
        }
        
        return YES;                 // 不是以上情况的字符全部超过三个字节,做Emoji处理
    }
    
    return NO;
}

- (NSString *)converStrEmoji:(NSString *)emojiStr
{
    __block NSString *muStr = [[NSString alloc]initWithString:emojiStr];
    [emojiStr enumerateSubstringsInRange:NSMakeRange(0, emojiStr.length) options:NSStringEnumerationByComposedCharacterSequences usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
        if ([substring containEmoji]) {
            muStr = [muStr stringByReplacingOccurrencesOfString:substring withString:@""];
        }
    }];
    return muStr;
}



+(BOOL)nowIsEmojiKeyBorad
{
    for (UITextInputMode *keyboardInputMode in [UITextInputMode activeInputModes]) {
        
        if ([[keyboardInputMode valueForKey:@"primaryLanguage"] isEqualToString:@"emoji"]) {
            //ios7以上
            NSNumber *isDisplayed = [keyboardInputMode valueForKey:@"isDisplayed"];
            
            if ([isDisplayed boolValue] == YES) {
                
                return YES;
                
            }
            
        }
        
    }
    
    return NO;
}


@end
